package 泛型;
/*
了解了Java泛型的实现方式——擦拭法，我们就知道了Java泛型的局限：
局限一：<T>不能是基本类型，例如int，因为实际类型是Object，Object类型无法持有基本类型：
Pair<int> p = new Pair<>(1, 2); // compile error!
局限二：无法取得带泛型的Class。观察以下代码：

public class Main {
    public static void main(String[] args) {
        Pair<String> p1 = new Pair<>("Hello", "world");
        Pair<Integer> p2 = new Pair<>(123, 456);
        Class c1 = p1.getClass();
        Class c2 = p2.getClass();
        System.out.println(c1==c2); // true
        System.out.println(c1==Pair.class); // true

    }
}

class Pair<T> {
    private T first;
    private T last;
    public Pair(T first, T last) {
        this.first = first;
        this.last = last;
    }
    public T getFirst() {
        return first;
    }
    public T getLast() {
        return last;
    }
}

因为T是Object，我们对Pair<String>和Pair<Integer>类型获取Class时，获取到的是同一个Class，也就是Pair类的Class。

换句话说，所有泛型实例，无论T的类型是什么，getClass()返回同一个Class实例，因为编译后它们全部都是Pair<Object>。

局限三：无法判断带泛型的类型：

Pair<Integer> p = new Pair<>(123, 456);
// Compile error:
if (p instanceof Pair<String>) {
}
原因和前面一样，并不存在Pair<String>.class，而是只有唯一的Pair.class。

局限四：不能实例化T类型：

public class Pair<T> {
    private T first;
    private T last;
    public Pair() {
        // Compile error:
        first = new T();
        last = new T();
    }
}
上述代码无法通过编译，因为构造方法的两行语句：

first = new T();
last = new T();
擦拭后实际上变成了：

first = new Object();
last = new Object();
这样一来，创建new Pair<String>()和创建new Pair<Integer>()就全部成了Object，显然编译器要阻止这种类型不对的代码。

要实例化T类型，我们必须借助额外的Class<T>参数：

public class Pair<T> {
    private T first;
    private T last;
    public Pair(Class<T> clazz) {
        first = clazz.newInstance();
        last = clazz.newInstance();
    }
}
上述代码借助Class<T>参数并通过反射来实例化T类型，使用的时候，也必须传入Class<T>。例如：

Pair<String> pair = new Pair<>(String.class);
因为传入了Class<String>的实例，所以我们借助String.class就可以实例化String类型。
*/
/*
public class 泛型实例化<T> {
    T mType;

    public 泛型实例化(Class<T> clz) throws InstantiationException, IllegalAccessException {
   	 try {
        //mType = clz.newInstance(); //已弃用，newInstance()为无参构造，改为下式
        mType = clz.getDeclaredConstructor().newInstance();//为了方便，这里省略了 try...catch代码块
   	 }catch(Exception e) {}
    }

    static class Trainer {//静态内部类
        public void print() {
            System.out.println("Trainer创建成功");
        }
    }

    public static void main(String[] args) throws Throwable {
   	 //Trainer tra=new Trainer();
   	 //泛型实例化<Trainer> trainerPractice = new 泛型实例化<Trainer>((Class<Trainer>) tra.getClass());
   	 泛型实例化<Trainer> trainerPractice = new 泛型实例化<Trainer>(Trainer.class);
        Trainer trainer = trainerPractice.mType;
        trainer.print();
    }
}
*/
/*
静态类与内部类的区别
内部类

1、内部类拥有普通类的所有特性，也拥有类成员变量的特性
2、内部类可以访问其外部类的成员变量，属性，方法，其它内部类

静态内部类

1、只有内部类才能声明为static，也可以说是静态内部类
2、只有静态内部类才能拥有静态成员，普通内部类只能定义普通成员
3、静态类跟静态方法一样，只能访问其外部类的静态成员
4、如果在外部类的静态方法中访问内部类，这时候只能访问静态内部类
内部类实例化

1.访问内部类，必须使用：外部类.内部类，OutClass.InnerClass
2.普通内部类必须绑定在其外部类的实例上
3.静态内部类可以直接 new
*/
/*
使用泛型能带来很多的好处，比如最明显的就是省去了强制类型转换。但同时也带来了一些麻烦。例如，如果代码中有下面这样的需求
public class Practice<T> {
    T mParam;
    public Practice() {
        mParam = new T();//报错 Type parameter 'T' cannot be instantiated directly
    }
}

产生这种错误的原因主要有两点
1.泛型擦除
2.编译器不知道该类型是否有默认的构造器
通常为了使用泛型，同时还要避免这样的问题出现，会花很多时间寻找可替代方案。有没有同时兼顾的办法呢？答案是肯定的。利用Class对象就可以达到想要的实现的效果，看代码
public class Practice<T> {
    T mType;

    public Practice(Class<T> clz) {
        mType = clz.newInstance(); //为了方便，这里省略了 try...catch代码块
    }

    static class Trainer {
        public void print() {
            System.out.println("Trainer创建成功");
        }
    }

    public static void main(String[] args) {
        Practice<Trainer> trainerPractice = new Practice<>(Trainer.class);
        Trainer trainer = trainerPractice.mType;
        trainer.print();
    }
}


输出结果：Trainer创建成功

看到这有没有点小激动，既使用了泛型，同时创建了一个“泛型实例”。不过，先别急着激动，当把自己定义的Trainer类替换成Integer上述代码就会报错了，原因是Integer没有
任何默认的构造器。这个错误不是 在编译期捕获的，所以当我们使用Integer运行的时候就会报错
Caused by: java.lang.NoSuchMethodException: java.lang.Integer.<init>()
	at java.lang.Class.getConstructor0(Class.java:3082)
	at java.lang.Class.newInstance(Class.java:412)
	... 7 more

从报错信息也可以看出，找不到初始化的方法。
带着这个问题，我们观察一下上面最初提出的解决问题的方案，不论是什么类型，都将实例化的过程都用了同一种的实例化方法
mType = clz.newInstance();

这样做，即便不使用Integer，当我们需要把某个自定义的类默认构造方法声明为private时，上面的方法也不适用了。那怎样解决这个问题呢？答案就是java编程的六大
基本原则之一的面向接口编程。接口的好处就是把具体实现分割开，针对不同的需求，各自实现自己想要的结果，互相之间不干扰。
有了思路，下面就开始解决这个问题。首先声明一个接口，接口里声明一个方法，此方法用来创建泛型所代表类的实例

interface Creator<T> {
    T create();
}

//这里还是以自定义的Trainer类实例化为例。创建一个用来把泛型类和实例化结果关联起来的工厂类，工厂类不关心具体的实例化过程，它只负责将实例化结果赋值给泛型类。
//工厂类的构造方法是一个泛型方法，泛型的边界是接口Creator，同时构造方法的参数是实现了Creator接口的子类。在子类的create()方法里是具体的类实例化过程。
class Factory<T> {
    private T t;
    public <C extends Creator<T>> Factory(C creator) {
        t = creator.create();
    }
}

在Factory类里，通过调用实现了Creator接口子类的create()方法创建实例。接下来，创建一个TrainerCreator类来实现Creator接口
public class TrainerCreator implements Creator<Trainer> {
    @Override
    public Trainer create() {
        return new Trainer();
    }

    public static void main(String[] args) {
        Factory<Trainer> factory = new Factory<>(new TrainerCreator());
        Trainer trainer = factory.getT();
        System.out.println(trainer.toString());
    }
}
输出结果：Trainer

为了方便我把Trainer类的代码也贴出来
class Trainer {
    @Override
    public String toString() {
        return "Trainer";
    }
}

至此，改进版的代码就完成了。可以看到，类的实例化也就是new动作放在了具体的实现类里，而Factory的作用类似于适配器一样，它负责将Creator接口实现类的create()方
法返回的类实例赋值给泛型类。这样做，大大降低了代码的耦合度。假如，此时需要一个Integer类型的值。那么只需要实现Creator接口创建一个新的用于创建Integer实例的
实现类就可以了
*/
/*
public class 泛型实例化 implements Creator<Integer> {
    @Override
    public Integer create() {
        return 1;
    }

    public static void main(String[] args) {
        Factory<Integer> factory = new Factory<>(new IntegerCreator());
        Integer integer = factory.getT();
        System.out.println(integer.toString());
    }
}
class Trainer {
   @Override
   public String toString() {
       return "Trainer";
   }
}
interface Creator<T> {
   T create();
}
class Factory<T> {
   private T t;
   public <C extends Creator<T>> Factory(C creator) {
       t = creator.create();
   }
}
//输出结果：1
*/